昨天好好睡了一覺,今天的精神有比較好一些。
昨天頭痛了一整天......
原本今天的課程是要講建構式和繼承的,但我發現光是和建構式相關的東西就可以寫很多很多。
所以今天這一堂課,我們就專門講建構式好了。
先來一段程式碼:
//基本是建議使用mysqli或是pdo,不過目前市面書籍很多還是只介紹mysql指令集
mysql_connect('localhost','root','password');
mysql_select_db('mydb');
mysql_query("SET NAMES 'utf8'");
$sql = "SELECT * FROM `user` WHERE name = 'sam'";
$result = mysql_query($sql);
if($result != false)
while($row = mysql_fetch_object($result)){
echo $row->name.'<br/>';
}
}
觀眾:ㄟㄟㄟ!你給我等等,你是在上物件導向,說要教建構式,你寫個mysql讀取的程式碼幹嘛?
這……這當然和我的主題有關係啦。
回到物件這邊:
各位都知道,線上遊戲的技能有分「主動技」和「被動技」。
觀眾:你離題了......
所謂的主動技能是玩家要自己去發動的技能,而所謂的被動技就是指會自動去處理的技能。
前面所介紹的物件導向的方法(method),都是必須外部去呼叫執行該物件的方法才會進行動作。
但有的時候會有一種狀況就是每一次執行該物件都「必須」先執行某個或是一連串的動作。
這其實相當麻煩。
像上面那一段mysql語法,可能一些人會想說那我用函式包起來,以候呼叫函式就好了。
於是就變成了這樣:
function db_connect($host,$username,$password,$dbs){
mysql_connect($host,$username,$password);
mysql_select_db($db);
mysql_query("SET NAMES 'utf8'");
}
db_connect('localhost','root','password','mydb');
是的,大致上來說這樣看起來算是頗為完美。
也有人乾脆就整個參數寫在程式內直接叫db_connect();
可問題是你每次去執行sql資料庫時,若要像第一段那樣來上一段。
相對的很麻煩。
用上述的函式處理其實很ok,只不過在更詳細的物件操作上,有絕對比較好的簡單做法。
我們將上面的code轉換成物件。
class db{
public $host = 'localhost';
public $username = 'root';
public $password = 'password';
public $database = 'mydb';
function __construct(){
$this->sql_connect();
$this->sql_database();
$this->set_db_encode();
}
function sql_connect(){
return @mysql_connect($this->host,$this->username,$this->password);
}
function sql_database(){
return @mysql_select_db($this->database);
}
function set_db_encode(){
return mysql_query("SET NAMES 'utf8'");
}
}
$db = new db;
在這個code中,我們看到了一個很特別的方法名稱:__construct();
這個__construct()就是我今天的主體:建構式。
觀眾:你講了那麼大一串,現在才講到?不過......什麼是建構式?
這邊就如我先前說的,你想要使用sql功能,每一次都要做連線、選資料庫、編碼。
這樣每次寫實在很累。包成函式是不錯的方法,但封裝成物件有他接下來使用上的絕對優勢。
而所謂的建構式,其意義為:
『當物件生成的同時,強制預先去實作執行物件本身的功能。』
也就是說,當我$db = new db;時。
sql_connect(),sql_database()及set_db_encode()這三個方法會被強制先執行。
這邊我得提一些基本的編寫概念。
基本上建構式(亦稱建構子)並沒有規範你一定得怎麼寫裡面的程式碼。
你要在裡面echo、if、for、while、撈資料庫、做壞事、搞破壞.................
都跟你平常正常寫程式是一樣的。
但是,基於讓程式碼可維護。
也因此對於建構式就會有基本二個原則:
1.建構式僅用來做為設置預設屬性值的存在。
例:
class demo{
function __construct($name){
$this->name = $name;
}
}
$demo = new demo('sam');
※先前未交建構式前,各位會發現我new物件時並不會在物件的名稱加上括號:
$demo = new demo;
但是如果你的建構式中是必須給予參數的情形下,你就必須加上括號:
$demo = new demo('sam');
如果你本身對於這方面加與不加不是很肯定,其實我建議你可以全都加括號:
$demo = new demo();
2.建構式僅用來做為預載函式功能執行的存在:
class demo
function __construct(){
$this->Action();
}
function Action(){
}
}
除了這二件事情外,僅可能的不要拿建構式去做其他多餘的事。
※在某些framework,因為建構式的內容應該要繼承於其父類別的建構式,通常為了避免建構
式的父體子體產生誤會,我習慣在子物件的建構式用以下的做法:
class demo extends CI_Controller(){
function __construct(){
parent::__construct();
$this->_init();
}
private function _init(){
//這才是真正寫建構式要預載功能的地方。
}
}
因為這邊還沒講到繼承,上面的code只是說明一個編寫習慣。
這個code在講繼承時會再出現。
了解上面二點在建構式中基於編程式碼易讀易維護的方法後。
相信你們就比較了解建構式在物件中所扮演的角色。
接下來,我要全面的改寫第一段code的內容:
class db{
public $host = 'localhost';
public $username = 'root';
public $password = 'password';
public $database = 'mydb';
public $result;
function __construct(){
$this->sql_connect();
$this->sql_database();
$this->set_db_encode();
}
function sql_connect(){
return @mysql_connect($this->host,$this->username,$this->password);
}
function sql_database(){
return @mysql_select_db($this->database);
}
function set_db_encode(){
return mysql_query("SET NAMES 'utf8'");
}
function query($sql_string){
$result = mysql_query($sql_string);
$query = new db_query($result);
return $query;
}
}
class db_query{
private $result;
function __construct($result){
$this->result = $result;
}
function result(){
$query = array();
if($this->result != false){
while($row = mysql_fetch_object($this->result)){
$query[] = $row;
}
return $query;
}
return false;
}
}
$db = new db;
$query = $db->query("SELECT * FROM `user` WHERE name = 'sam'");
foreach($query->result() as $row){
echo $row->name.'<br/>';
}
這一段code可能複雜了些。
因為他是使用了預載sql連線以及特別的db_query資料查詢法。
但是你可以發現最後的執行就只是實體化db,然後將sql字串交給db物件的query處理。
最後db的query會回傳一個資料集合的物件。
你可以直接從資料物件中取得資料。
(事實上整個db能做的事情是非常多的,透過query物件來取得各種query或是row是很方便的事情,而db本身也可以直接去處理insert、update、delete…等等三功能。)
我們看到了db物件的建構式來做資料庫連線。
然後將產生的$result傳到query物件,而query物件的建構式就直接指定了$this->result這個屬性的值,對於後續的許許多多處理就很輕易的能夠解決。
後記:這次的課程我想我用了較為複雜的mysql來解釋建構式,也帶了點處理sql方法的意味。
但我想或許有些人覺得複雜了一些些。因此這邊我用比較白話的文字來做個總結:
『__construct()建構式,其目的在於當物件被實體化的同時,預先執行要一同載入或執行的功能』。
另外相關於建構式還有一個叫做解構式的東西__destruct();通常因為物件執行完就會被自動銷毀所以我就沒有提到解構式,將來有機會再稍做說明。
再來有關同名函式也是建構式這件事這邊我就隨便帶過了,因為PHP5開始我們強烈建議使用__construct()而不要使用同物件名的函式。所以下面這個例子也是建構式,但不再建議使用。
class demo{
function demo(){
↑↑↑↑同物件名稱所以這一段也是建構式。
}
}
明天就要來講繼承了。這也是非常重要的課題。
物件的多型全靠繼承來實作了。
值日生記得擦黑板,倒垃圾。放學回家要乖乖的路上別亂跑。
過馬路要看紅綠燈,記得扶老太太過馬路。
大大為何不早兩個月來參加鐵人賽了啊~~~
因為我.............壓根本曉得有這件事。
不曉得......
請問為什麼db_query要預載 __construct 這一段啊?
我測試之後,少了這一段,程式就跑不起來
不太知道這一段的功用是什麼。
<pre class="c" name="code">class db_query{
private $result;
function __construct($result){
$this->result = $result;
}
還有,為什麼function result()不能直接變成 function result($result),把變數值帶進來?
而要上述的程式才可以成功?
不好意思,不了解這兩個問題,再請好心人仕幫忙回答一下,謝謝。
如果是要問為什麼需要獨立出db_query類別,基本上這是「設計」的問題啦。(這個就需要樓主來回答了)
另外,我想樓主只是示範__construct的使用,所以簡化了範例。他設計的範例,在要支援不同資料庫時,會更容易明白為什麼這樣設計。基本上db_query類別可以用來封裝處理各種資料庫處理query result的邏輯,當然這樣設計會複雜一點。
<pre class="c" name="code">
interface idb_query {
public function result();
}
class mysql_db_query implements idb_query{
private $result;
function __construct($result) {
$this->$result = $result;
}
public function result() {
....
return $array;
}
}
如果是這樣設計,就不會透過result($result)來呼叫,因為對不同的idb_query實作來說,$result是完全不一樣的物件。而對使用者來說,他只需要知道db_query如何操作,並不需要知道$result的型別,這個資訊應該要封裝起來的。所以db_query是在db::query()中生成並回傳。